const PENDING = 'pending' //等待状态
const FULFILLED = 'fulfilled' //成功
const REJECTED = 'rejected' //拒绝

class MyPromise {
  constructor(exec) {
    try {
      exec(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }

  }

  status = PENDING;
  value = undefined;
  reason = undefined;
  successCallback = [];
  failCallback = [];

  resolve(value) {
    //调用resolve,判断状态，如果是等待状态则改成成功，否则不操作，实现满足状态一旦改变不可再改变的要求
    if (status === PENDING) {
      this.status = FULFILLED
      this.value = value;
      //当successCallback数组中有成功调用值函数时，循环执行，直到都执行完
      while (successCallback.length) this.successCallback.shift()()
    }
  }

  reject(reason) {
    //调用resolve,判断状态，如果是等待状态则改成失败，否则不操作，实现满足状态一旦改变不可再改变的要求
    if (status === PENDING) {
      this.status = REJECTED
      this.reason = reason;
      //当failCallback数组中有失败调用值函数时，循环执行，直到都执行完
      while (failCallback.length) this.failCallback.shift()()
    }

  }

  then(successCallback, failCallback) {
    //要实现then的链式调用，那么then要返回出一个promise对象
    let promise2 = new MyPromise((resolve, reject) => {
      if (status === FULFILLED) {
        /**
         * 如何阻止promise循环调用本身？通过判断回调返回值为promise对象时，这个对象是否是当前then返回出去的对象promise2
         * 这里为何要用setTimeout?因为promise2要完成实例后才能拿到，通过setTimeout先挂起到队列，等主程走完再来执行，这时promise2就存在了
         */
        setTimeout(() => {
          try {
            let x = successCallback(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }

        }, 0)
      } else if (status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }

        }, 0)
      } else {
        /**
        * 等待状态，因为调用then时then立即执行，但是没有执行resolve或者reject，所以是还在等待这时不知道是成功还是失败，
        * 等到异步结束来调用resolve或者reject知道，此时then已经执行完毕被回收，就没有回调函数，所以这时需要将回调函数存起来，
        * 等异步结束执行resolve或者reject时再使用
        */
        successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }

          }, 0)
        })
        failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
      }
    })
    return promise2;
  }

  //
  finally(callback) {
    return this.then(value => {
      //如果callback里返回一个promise对象，就调用静态方法reasolve处理
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }

  catch(failCallback) {
    return this.then(undefined, failCallback)
  }

  //处理数组内所有数据，包括内部的promise对象
  static all(array) {
    let result = [];
    let count = 0;

    /**
     * all方法需要返回一个promise对象，这样才会可以继续用then进行数据传递
     */
    return new MyPromise((resolve, reject) => {

      function addData(key, value) {
        result[key] = value;
        //因为存在promise中异步返回的情况，所以需要等待所有值都收集完成才将结果集resolve出去，每次调用addData进行累加，直到结果集的数组长度等于输入的数组长度
        count++;
        if (count === array.length) {
          resolve(result);
        }
      }

      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        //判断数组里是否有promise对象，有的话将成功回调的结果放入结果集，不是promises对象的话将值直接放入结果集
        if (current instanceof MyPromise) {
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          addData(i, array[i])
        }
      }

    })

  }
  //静态方法处理返回值为promise对象的情况,如果不是promise对象，则生成一个promise对象返回,让then的链式调用可以维持
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value.then()
    } else {
      return new MyPromise(resolve => resolve(value))
    }
  }
}

//处理promise调用自身问题，通过判断禁止调用自身
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('promise禁止循环调用自身'))
  }

  //判断返回值x是否是promise对象,是promise对象的话调用then方法
  if (x instanceof MyPromise) {
    x.then(value => resolve(value), reason => reject(reason))
  } else {
    resolve(x)
  }
}
module.exports = MyPromise